#include "Basic/UnitTest.h"
#include "Basic/String.h"
#include "lex.h"
#include "expression.h"
#include <stdlib.h>
#include <stdio.h>
using namespace zl;
using namespace logo;

std::string DumpTokenType(tokentype type)
{
	std::string name = "";
	switch (type)
	{
	case tokentype::UNKNOW:
		name = "UNKNOW";
		break;
	case tokentype::SEP:
		name = "SEP";
		break;
	case tokentype::NUMERICAL:
		name = "NUMERICAL";
		break;
	case tokentype::ADD:
		name = "ADD";
		break;
	case tokentype::SUB:
		name = "SUB";
		break;
	case tokentype::MUL:
		name = "MUL";
		break;
	case tokentype::DIV:
		name = "DIV";
		break;
	case tokentype::GT:
		name = "GT";
		break;
	case tokentype::LT:
		name = "LT";
		break;
	case tokentype::GE:
		name = "GE";
		break;
	case tokentype::LE:
		name = "LE";
		break;
	case tokentype::CMD:
		name = "CMD";
		break;
	case tokentype::EQ:
		name = "EQ";
		break;
	case tokentype::VAR:
		name = "VAR";
		break;
	case tokentype::KEYWORD:
		name = "KEYWORD";
		break;
	case tokentype::DEF:
		name = "DEF";
		break;
	case tokentype::OPENBRACKETS:
		name = "OPENBRACKETS";
		break;
	case tokentype::CLOSEBRACKETS:
		name = "CLOSEBRACKETS";
		break;
	case tokentype::OPENMIDBRACKETS:
		name = "OPENMIDBRACKETS";
		break;
	case tokentype::CLOSEMIDBRACKETS:
		name = "CLOSEMIDBRACKETS";
		break;
	}
	return name;
}

void DumpToken(Token token)
{
#ifdef _MSC_VER
	printf_s("token value:%s row:%d col:%d type:%s ",wtoa(token.value).c_str(),
		     token.row,token.col,DumpTokenType(token.type).c_str());
#else
	printf("token value:%s row:%d col:%d type:%s ",wtoa(token.value).c_str(),
		token.row, token.col, DumpTokenType(token.type).c_str());
#endif
}

void DumpLexError(LexError info)
{
#ifdef _MSC_VER
	printf_s("Lex Error:");
#else
	printf("Lex Error:");
#endif
	DumpToken(info.token);
#ifdef _MSC_VER
	printf_s("%s\n", wtoa(info.message).c_str());
#else
	printf("%s\n", wtoa(info.message).c_str());
#endif
}

void TestLex(const std::wstring& input)
{
	List<LexError> errs;
	Ptr<CodeFile> ptr = CodeFile::Parse(input, errs);
	for(auto info:errs)
	{
		DumpLexError(info);
	}
	for(auto line:ptr->lines)
	{
		zint index = 0;
		for(auto token:line.tokens)
		{
			DumpToken(token);
#ifdef _MSC_VER
			printf_s("\n");
#else
			printf("\n");
#endif
		}
	}
}


void TestParseCmdExpression(const std::wstring& input)
{
	List<LexError> errs;
	Ptr<CodeFile> ptr = CodeFile::Parse(input, errs);
	Dictionary<std::wstring, AssignmentExpression> env;
	List<ExpressionError> errors;
	for (auto line:ptr->lines)
	{
		zint index = 0;
		while (index<line.tokens.size())
		{
			Ptr<Expression> code = ParseCmdExpression(line.tokens, index, env, errors);
			if (errors.size()>0)
			{
				for (auto er:errors)
				{
#ifdef  _MSC_VER
					printf_s("err:");
#else
					printf("err:");
#endif
					DumpToken(er.info);
#ifdef _MSC_VER
					printf_s("msg:%s\n",wtoa(er.message).c_str());
#else
					printf("msg:%s\n",wtoa(er.message).c_str());
#endif
				}
				break;
			}
			if (code == nullptr) break;
			CmdExpression* ptr = dynamic_cast<CmdExpression*>(code.Obj());
#ifdef      _MSC_VER
			printf_s("cmd:%s", wtoa(ptr->name).c_str());
#else
			printf("cmd:%s", wtoa(ptr->name).c_str());
#endif
#ifdef      _MSC_VER
			printf_s(" args:");
#else
			printf(" args:");
#endif
			for(auto v:ptr->args)
			{
#ifdef      _MSC_VER
				printf_s("%f ", v);
#else
				printf("%f ",v);
#endif
			}
#ifdef      _MSC_VER
			printf_s("\n");
#else
			printf("%f ",v);
#endif
		}
	}
}

void TestParseAssignmentExpression(const std::wstring& input)
{
	List<LexError> errs;
	Ptr<CodeFile> ptr = CodeFile::Parse(input, errs);
	Dictionary<std::wstring, AssignmentExpression> env;
	List<ExpressionError> errors;
	for (auto line:ptr->lines)
	{
		zint index = 0;
		Ptr<Expression> code = ParseAssignmentExpression(line.tokens, index, env, errors);
		AssignmentExpression* ptr = dynamic_cast<AssignmentExpression*>(code.Obj());
#ifdef  _MSC_VER
		printf_s("name: %s result:%f\n",wtoa(ptr->name).c_str(),ptr->value);
#else
		printf("name: %s result:%f\n",wtoa(ptr->name).c_str(),ptr->value);
#endif
	}
}

void TestParseFuncExpression(const std::wstring& input)
{
	List<LexError> errs;
	Ptr<CodeFile> ptr = CodeFile::Parse(input, errs);
	Dictionary<std::wstring, AssignmentExpression> env;
	List<ExpressionError> errors;
	for (auto line:ptr->lines)
	{
		zint index = 0;
		while (index<line.tokens.size())
		{
			Ptr<Expression> code = ParseUseFuncExpression(line.tokens, index, env, errors);
			if (errors.size()>0)
			{
				for (auto er:errors)
				{
#ifdef  _MSC_VER
					printf_s("err:");
#else
					prinf("err:");
#endif
					DumpToken(er.info);
#ifdef  _MSC_VER
					printf_s(" msg:%s\n",wtoa(er.message).c_str());
#else
					printf(" msg:%s\n",wtoa(er.message).c_str());
#endif
				}
				break;
			}
			if (code == nullptr) break;
			FuncExpression* ptr = dynamic_cast<FuncExpression*>(code.Obj());
#ifdef    _MSC_VER
			printf_s("cmd:%s args:",wtoa(ptr->name).c_str());
#else
			printf("cmd:%s args:",wtoa(ptr->name).c_str());
#endif
			for (auto v:ptr->args)
			{
#ifdef      _MSC_VER
				printf_s("%f ", v);
#else
				printf("%f ",v);
#endif
			}
#ifdef     _MSC_VER
			printf_s("\n");
#else
			printf("\n");
#endif
		}
	}
}

void TestParseCompareExpression(const std::wstring& input)
{
	List<LexError> errs;
	Ptr<CodeFile> ptr = CodeFile::Parse(input, errs);
	Dictionary<std::wstring, AssignmentExpression> env;
	List<ExpressionError> errors;
	for (auto line:ptr->lines)
	{
		zint index = 0;
		Ptr<Expression> code = ParseCompareExpression(line.tokens, index, env, errors);
		if (errors.size()>0)
		{
			for (auto er:errors)
			{
#ifdef   _MSC_VER
				printf_s("err:");
#else
				printf("err:");
#endif
				DumpToken(er.info);
#ifdef   _MSC_VER
				printf_s(" msg:%s\n",wtoa(er.message).c_str());
#else
				printf(" msg:%s\n",wtoa(er.message).c_str());
#endif
			}
			break;
		}
		else
		{
			CompareExpression* ptr = dynamic_cast<CompareExpression*>(code.Obj());
#ifdef _MSC_VER
			printf_s("result:%s\n", (ptr->value == true ? "true" : "false"));
#else
			printf("result:%s\n", (ptr->value == true ? "true" : "false"));
#endif
		}
	}
}

void ShowExpression(Ptr<Expression> expression)
{
	switch (expression->type)
	{
	case ExpressionType::Arithmetic:
	{
      ArithmeticExpression* ptr = dynamic_cast<ArithmeticExpression*>(expression.Obj());
#ifdef _MSC_VER
	  printf_s("type:arithmetic result:%f\n",ptr->value);
#else
	  printf("type:arithmetic result:%f\n",ptr->value);
#endif
	}
		break;
	case ExpressionType::Assignment:
	{
	  AssignmentExpression* ptr = dynamic_cast<AssignmentExpression*>(expression.Obj());
#ifdef _MSC_VER
	  printf_s("type:assignment name:%s result:%f\n",wtoa(ptr->name).c_str(),ptr->value);
#else
	  printf("type:assignment name:%s result:%f\n",wtoa(ptr->name).c_str(),ptr->value);
#endif
	}
		break;
	case ExpressionType::Cmd:
	{
	  CmdExpression* ptr = dynamic_cast<CmdExpression*>(expression.Obj());
#ifdef _MSC_VER
	  printf_s("type:cmd name:%s args:",wtoa(ptr->name).c_str());
#else
	  printf("type:cmd name:%s args:",wtoa(ptr->name).c_str());
#endif
      for(auto arg:ptr->args)
  	  {
#ifdef _MSC_VER
	    printf_s("%f ",arg);
#else
		  printf("%f ",arg);
#endif
	   }
#ifdef _MSC_VER
       printf_s("\n");
#else
	   printf("\n");
#endif
	}
		break;
	case ExpressionType::Func:
	{
	      FuncExpression* ptr = dynamic_cast<FuncExpression*>(expression.Obj());
#ifdef _MSC_VER
	      printf_s("type:func name:%s args:",wtoa(ptr->name).c_str());
#else
	      printf("type:func name:%s args:",wtoa(ptr->name).c_str());
#endif
	   for(auto arg:ptr->args)
	   {
#ifdef  _MSC_VER
		   printf_s("%f ",arg);
#else
		   printf("%f ",arg);
#endif
	   }
#ifdef  _MSC_VER
	   printf_s("\n");
#else
	   printf("\n");
#endif
	}
		break;
	case ExpressionType::Compare:
	{
		CompareExpression* ptr = dynamic_cast<CompareExpression*>(expression.Obj());
#ifdef _MSC_VER
		printf_s("type:compare result:%s\n", (ptr->value == true ? "true" : "false"));
#else
		printf("type:compare result:%s\n", (ptr->value == true ? "true" : "false"));
#endif
	}
		break;
	case ExpressionType::Loop:
	{
	    LoopExpression* ptr = dynamic_cast<LoopExpression*>(expression.Obj());
#ifdef _MSC_VER
		printf_s("type:loop loops:%d\n",ptr->loops);
#else
		printf("type:loop loops:%d\n",ptr->loops);
#endif
		for(auto e:ptr->proc)
		{
		   ShowExpression(e);
		}
	}
		break;
	case ExpressionType::Select:
	{
		SelectExpression* ptr = dynamic_cast<SelectExpression*>(expression.Obj());
#ifdef _MSC_VER
		printf_s("type:select result:%s\n",(ptr->value == true ? "true" : "false"));
#else
		printf("type:select result:%s\n",(ptr->value == true ? "true" : "false"));
#endif
	    for(auto e:ptr->tbranch)
		{
		   ShowExpression(e);
		}
		for(auto e:ptr->fbranch)
		{
		   ShowExpression(e);
		}
	}
		break;
	case ExpressionType::Defunc:
	{
		DefFuncExpression* ptr = dynamic_cast<DefFuncExpression*>(expression.Obj());
#ifdef _MSC_VER
		printf_s("type:deffunc start:%d end:%d\n",ptr->start,ptr->end);
#else
		printf("type:deffunc start:%d end:%d\n",ptr->start,ptr->end);
#endif
		for(auto name:ptr->args)
		{
#ifdef _MSC_VER
			printf_s("args:%s\n",wtoa(name).c_str());
#else
			printf("args:%s\n",wtoa(name).c_str());
#endif
		}
	}
		break;
	}
}


void TestParseUnit(const std::wstring &input)
{
	List<LexError> errs;
	Ptr<CodeFile> ptr = CodeFile::Parse(input, errs);
	Dictionary<std::wstring, AssignmentExpression> env;
	List<ExpressionError> errors;
	List<Ptr<Expression>> elist;
	for(auto line:ptr->lines)
	{
		zint index = 0;
		ParseUnit(line.tokens, index, elist, env, errors);
	}
	for(auto e:errors)
	{
#ifdef _MSC_VER
		printf_s("err:");
#else
		printf("err:");
#endif
		DumpToken(e.info);
#ifdef _MSC_VER
		printf_s(" msg:%s\n", wtoa(e.message).c_str());
#else
		printf(" msg:%s\n", wtoa(e.message).c_str());
#endif
	}
	for(auto ex:elist)
	{
		ShowExpression(ex);
	}
}

void TestParseDefFunc(const std::wstring &input)
{
	List<LexError> errs;
	Ptr<CodeFile> ptr = CodeFile::Parse(input, errs);
	Dictionary<std::wstring, AssignmentExpression> env;
	List<ExpressionError> errors;
	zint rows = 0;
	Ptr<Expression> func = ParseDefFuncExpression(ptr->lines, rows, env, errors);
	if (func != nullptr)
	{
		ShowExpression(func);
	}
	else
	{
		for (auto err : errors)
		{
			DumpToken(err.info);
#ifdef  _MSC_VER
			printf_s("message:\n",wtoa(err.message).c_str());
#else
			printf("message:\n",wtoa(err.message).c_str());
#endif
		}
	}
}

void TestParseArea(const std::wstring &input)
{
	List<LexError> errs;
	Ptr<CodeFile> ptr = CodeFile::Parse(input, errs);
	Dictionary<std::wstring, AssignmentExpression> env;
	List<ExpressionError> errors;
	zint rows = 0;
	List<Ptr<Expression>> expressions;
	env.add(L":EDGE", AssignmentExpression(L":EDGE", 100));
	ParseArea(ptr->lines, rows, expressions, env, errors);
	for(auto ex:expressions)
	{
		ShowExpression(ex);
	}
}

void TestParseArithmeticExpression(const std::wstring &input)
{
	List<LexError> errs;
	Ptr<CodeFile> ptr = CodeFile::Parse(input, errs);
	Dictionary<std::wstring, AssignmentExpression> env;
	env.add(L":E", AssignmentExpression(L":E", 10));
	List<ExpressionError> errors;
	for(auto line:ptr->lines)
	{
		zint index = 0;
		Ptr<ArithmeticExpression> code = ParseArithmeticExpression(line.tokens, index, env, errors);
		if (errors.size()>0)
		{
			for(auto er:errors)
			{
#ifdef _MSC_VER
				printf_s("err:");
#else
				printf("err:");
#endif
				DumpToken(er.info);
#ifdef _MSC_VER
				printf_s("msg:%s\n",wtoa(er.message).c_str());
#else
				printf("msg:%s\n", wtoa(er.message).c_str());
#endif
			}
		}
		else
		{
#ifdef _MSC_VER
			printf_s("index:%d\n", index);
#else
			printf("index:%d\n", index);
#endif
			if (code != nullptr)
			{
#ifdef _MSC_VER
				printf_s("result:%f\n", code->value);
#else
				printf("result:%f\n", code->value);
#endif
			}
		}
	}
}

TEST_CASE(TestLex)
{
	TestLex(L"5+6*(123+789)");
	TestLex(L"IF 5>6 FD 9 ELSE BK 20 STOP");
	TestLex(L"REPEAT 4[ FD 9]");
};

TEST_CASE(TestExpression)
{
	TestParseArithmeticExpression(L"4.5*(7-5)/20+8*9");
	TestParseAssignmentExpression(L":VARS 4.5*(7-5)/20+8*9+45/8");
	TestParseCompareExpression(L"4.5*(7-5)>10");
	TestParseCmdExpression(L"FD 9 BK 20");
	TestParseFuncExpression(L"FUNC 20 30 40");
	TestParseDefFunc(L"TO FUNC :A :B\nFD 90\nEND");
	TestParseUnit(L"IF 5<1 STOP");
	TestParseUnit(L"REPEAT 4[ FD 20 RT 90 ]");
	TestParseArea(L"IF 5<1 STOP\nREPEAT 4[FD:B RT 90]\nFD 20\nZFX 5/2 3\n");
}